package com.netty.demo.server.netty.udp;

import com.netty.demo.server.netty.constant.ConstantValue;
import com.netty.demo.server.netty.enums.RequestHeaderProtocol;
import com.netty.demo.server.netty.protocol.RequestProtocol;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.DatagramPacket;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.List;

/**
 * 请求解码器
 * <p>
 * 请求解码器
 * <pre>
 * 数据包格式
 * +——----——+——-----——+——----——+——----——+——-----——+
 * | 包头  :  协议版本  :  协议长度   :   消息id  :   发送时间   :  数据       |
 * +——----——+——-----——+——----——+——----——+——-----——+
 * </pre>
 * 协议开始标志 HEAD，为int类型的数据，16进制表示为0X110
 * 模块号2字节short
 * 命令号2字节short
 * 传输数据的长度  length int类型
 *
 * @author 余秀良
 * @version 1.0
 * <p>date: 2019年09月09日  14:42 </p>
 * <p>address: 四川成都 </p>
 */
@Slf4j
public class UdpRequestDecoder extends MessageToMessageDecoder<DatagramPacket> {
    /**
     * TODO 目前为测试数据, 以正式数据为准
     * 数据包基本长度
     * byte     1字节
     * short    2字节
     * int      4字节
     * long     8字节
     * char     2字节
     * float    4字节
     * double   8字节
     */
    private static final int BASE_LENGTH = 4 + 2 + 2 + 4;
    /**
     * 协议最大长度
     */
    private static final int MAX_LENGTH = 2048;

    @Override
    protected void decode(ChannelHandlerContext ctx, DatagramPacket msg, List<Object> out) {
        //获取对 DatagramPacket 中的数据（ByteBuf）的引用
        ByteBuf byteBuf = msg.copy().content();
        byte[] bytes = new byte[byteBuf.readableBytes()];
        byteBuf.readBytes(bytes);
        try {
            //帧头
            long packageHead = byteBuf.readUnsignedInt();
            if (packageHead != ConstantValue.PACKAGE_HEAD) {
                sendMessage(ctx, msg, "非法协议");
                return;
            }
            //可读长度readableBytes必须大于基本长度才处理
            if (byteBuf.readableBytes() >= BASE_LENGTH) {
                //防止socket字节流攻击
                if (byteBuf.readableBytes() > MAX_LENGTH) {
                    // 忽略所有可读的字节
                    byteBuf.skipBytes(byteBuf.readableBytes());
                    sendMessage(ctx, msg, "帧数据超长");
                    return;
                }
                //源逻辑id
                long sourceLogicId = byteBuf.readUnsignedInt();
                //目标逻辑id
                long targetLogicId = byteBuf.readUnsignedInt();
                //消息长度
                long length = byteBuf.readUnsignedInt();
                //消息类型
                short messageType = byteBuf.readUnsignedByte();
                //将消息id
                short messageId = byteBuf.readUnsignedByte();
                //获取读索引的当前位置
                int idx = byteBuf.readerIndex();
                //提取消息，从读索引开始，到最后为数据
                String body = byteBuf.slice(idx, byteBuf.readableBytes() - 4).toString(CharsetUtil.UTF_8);
                byteBuf.readerIndex(idx + body.length());
                //帧尾
                long packageEnd = byteBuf.readUnsignedInt();
                //构建一个新的 RequestProtocol 对象，并且将它添加到（已经解码的消息的）列表中
                RequestProtocol event = new RequestProtocol();
                event.setPackageHead(packageHead);
                RequestHeaderProtocol requestHeaderProtocol = RequestHeaderProtocol.value(messageType, messageId);
                if (requestHeaderProtocol != null) {
                    event.setProtocolHeader(requestHeaderProtocol.getValue());
                }
                event.setSourceLogicId(sourceLogicId);
                event.setTargetLogicId(targetLogicId);
                event.setLength(length);
                event.setMessageType(messageType);
                event.setMessageId(messageId);
                event.setData(body);
                event.setPackageEnd(packageEnd);
                event.setSourceAddress(msg.sender());
                event.setRecipient(msg);
                //作为本handler的处理结果，交给后面的handler进行处理
                out.add(event);
            }
        } catch (Exception e) {
            log.error("解码异常 {} {}", e.getMessage(), e.getLocalizedMessage());
            e.printStackTrace();
        } finally {
            byteBuf.clear();
            ReferenceCountUtil.release(byteBuf);
        }
    }

    private void sendMessage(ChannelHandlerContext ctx, DatagramPacket packet, String message) {
        //帧数据超长
        DatagramPacket datagramPacket = new DatagramPacket(Unpooled.copiedBuffer(message, CharsetUtil.UTF_8), packet.sender());
        ctx.channel().writeAndFlush(datagramPacket);
    }
}
